package org.microboot.data.config;

import com.google.common.collect.Maps;
import freemarker.ext.beans.BeansWrapperBuilder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.microboot.core.bean.ApplicationContextHolder;
import org.microboot.core.constant.Constant;
import org.microboot.data.basedao.BaseDao;
import org.microboot.data.factory.DataSourceFactory;
import org.microboot.data.factory.DruidDataSourceFactory;
import org.microboot.data.processor.DefaultTransactionManagerPostProcessor;
import org.microboot.data.processor.NonDefaultTransactionManagerPostProcessor;
import org.microboot.data.resolver.TemplateResolver;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @author 胡鹏
 * 在老版本中，microboot-framework-data内置了从库集群的负载均衡，从库节点退避以及回归等功能
 * 但作者在不断学习过程中，接触到了LVS+keepalived的方案
 * · LVS可以实现IP层面的负载均衡
 * · keepalived可以实现LVS的高可用，还能对LVS负载的real server节点进行健康检查，并让宕机的节点退避
 * 这些功能完全覆盖了microboot-framework-data内置的从库逻辑
 * 因此新版版中，将从库的负载均衡，退避等相关功能全部去掉了
 * 这部分功能在以后的架构中交由LVS+keepalived或其他类似功能去实现
 */
@Configuration
@DependsOn(Constant.APPLICATION_CONTEXT_HOLDER)
public class DataConfig {

    /**
     * 初始化PropertySourcesPlaceholderConfigurer
     *
     * 实现结果：
     * 情况一：当PropertySourcesPlaceholderConfigurer声明在其他配置类中时，DataSourceFactory中的@ConfigurationProperties注解可以读取到database.yml中的配置信息
     * 情况二：当PropertySourcesPlaceholderConfigurer和DataSourceFactory声明在一个配置类中时，Bean的name必须设置成propertySourcesPlaceholderConfigurer
     * 否则DataSourceFactory中的@ConfigurationProperties读取不到database.yml的配置信息
     *
     * 情况一的原因没找到，情况二的分析：
     *  1、首先可以确定PropertySourcesPlaceholderConfigurer被正常创建，并且database.yml也正常读取到了
     *  2、其次在PropertySourcesPlaceholderConfigurer的postProcessBeanFactory()中打断点发现被执行了两次
     *  从而发现SpringBoot自动装配默认会创建一个beanName为propertySourcesPlaceholderConfigurer的PropertySourcesPlaceholderConfigurer对象
     *  3、接着在DataSourceFactory中增加构造方法和setter/getter方法后，分别打上断点，然后开始跟踪源码进行分析
     *  DataSourceFactory的setter方法会在Spring的Bean初始化阶段被调用，调用位置如下：
     *      initializeBean() -> applyBeanPostProcessorsBeforeInitialization()
     *      -> ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization()
     *      -> ConfigurationPropertiesBinder#bind() -> ... -> ConfigurationPropertiesBinder#getBinder()
     *  在getBinder()中会返回ConfigurationPropertiesBinder的成员变量binder，如下：
     *      private Binder getBinder() {
     * 	    	if (this.binder == null) {
     * 	    		this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(),
     * 	    				getConversionService(), getPropertyEditorInitializer(), null,
     * 	    				ConfigurationPropertiesBindConstructorProvider.INSTANCE);
     *                    }
     * 	    	return this.binder;
     * 	    }
     *  但是debug发现此时binder对象不为null，但是全局查找后发现binder只有在这里初始化
     *  Binder构造方法的第二个参数是调用getPropertySourcesPlaceholdersResolver()来获取PropertySourcesPlaceholdersResolver对象，如下：
     *      private PropertySourcesPlaceholdersResolver getPropertySourcesPlaceholdersResolver() {
     * 	    	return new PropertySourcesPlaceholdersResolver(this.propertySources);
     *      }
     *  这个方法默认就是创建一个PropertySourcesPlaceholdersResolver对象，并且传入了this.propertySources
     *  随后发现this.propertySources是在ConfigurationPropertiesBinder的构造方法中被创建和初始化的，如下：
     *      ConfigurationPropertiesBinder(ApplicationContext applicationContext) {
     * 	    	this.applicationContext = applicationContext;
     * 	    	this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources();
     * 	    	this.configurationPropertiesValidator = getConfigurationPropertiesValidator(applicationContext);
     * 	    	this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext);
     *      }
     *  getPropertySources()方法如下：
     *      PropertySources getPropertySources() {
     * 	    	PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
     * 	    	if (configurer != null) {
     * 	    		return configurer.getAppliedPropertySources();
     *          }
     * 	    	MutablePropertySources sources = extractEnvironmentPropertySources();
     * 	    	Assert.state(sources != null,
     * 	    			"Unable to obtain PropertySources from PropertySourcesPlaceholderConfigurer or Environment");
     * 	    	return sources;
     * 	    }
     * 	①、如果getSinglePropertySourcesPlaceholderConfigurer()返回的PropertySourcesPlaceholderConfigurer对象不为null
     * 	那么会调用PropertySourcesPlaceholderConfigurer的getAppliedPropertySources()返回appliedPropertySources属性
     * 	这个appliedPropertySources属性在PropertySourcesPlaceholderConfigurer的postProcessBeanFactory()中初始化并保存了database.yml中的配置信息
     * 	②、如果getSinglePropertySourcesPlaceholderConfigurer()返回的PropertySourcesPlaceholderConfigurer对象为null
     * 	那么会调用extractEnvironmentPropertySources()方法获取MutablePropertySources对象，如下：
     * 	    private MutablePropertySources extractEnvironmentPropertySources() {
     * 	    	Environment environment = this.applicationContext.getEnvironment();
     * 	    	if (environment instanceof ConfigurableEnvironment) {
     * 	    		return ((ConfigurableEnvironment) environment).getPropertySources();
     *          }
     * 	    	return null;
     * 	    }
     * 	可以看到，这个方法从Environment中获取MutablePropertySources对象并返回，但此时Environment中没有保存database.yml配置文件中的信息
     *  那什么情况下，getSinglePropertySourcesPlaceholderConfigurer()会返回null呢？如下：
     * 	    private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
     * 	    	// Take care not to cause early instantiation of all FactoryBeans
     * 	    	Map<String, PropertySourcesPlaceholderConfigurer> beans = this.applicationContext
     * 	    			.getBeansOfType(PropertySourcesPlaceholderConfigurer.class, false, false);
     * 	    	if (beans.size() == 1) {
     * 	    		return beans.values().iterator().next();
     *          }
     * 	    	if (beans.size() > 1 && logger.isWarnEnabled()) {
     * 	    		logger.warn("Multiple PropertySourcesPlaceholderConfigurer beans registered " + beans.keySet()
     * 	    				+ ", falling back to Environment");
     *          }
     * 	    	return null;
     * 	    }
     * 可以看到这个方法的逻辑，它从BeanFactory容器中获取的PropertySourcesPlaceholderConfigurer类型的Bean对象
     * 如果只有一个，则返回，但如果超过1个，就返回null
     *
     * 补充：ConfigurationPropertiesBinder是在什么时候创建的呢？
     * 在ConfigurationPropertiesBinder的构造方法中打断点，调试一下可知：
     * createBeanInstance() -> instantiateUsingFactoryMethod() -> instantiateUsingFactoryMethod()
     * -> bw.setBeanInstance(instantiate(...)) -> this.beanFactory.getInstantiationStrategy().instantiate(...) -> Object result = factoryMethod.invoke(...)
     * -> ConfigurationPropertiesBinder内部类Factory的create() -> new ConfigurationPropertiesBinder() 创建了ConfigurationPropertiesBinder对象
     *
     * 总结：
     *  SpringBoot自动装配创建的PropertySourcesPlaceholderConfigurer的beanName为propertySourcesPlaceholderConfigurer
     *  如果自定义的PropertySourcesPlaceholderConfigurer的beanName与propertySourcesPlaceholderConfigurer不一致
     *  会导致Spring的BeanFactory容器中出现两个PropertySourcesPlaceholderConfigurer
     *  最终导致DataSourceFactory在初始化赋值时拿不到database.yml中的配置信息
     *
     * @param environment
     * @return
     */
    @Bean(name = "propertySourcesPlaceholderConfigurer")
    public PropertySourcesPlaceholderConfigurer initPropertySourcesPlaceholderConfigurer(Environment environment) throws Exception {
        String databaseLocation = environment.getProperty(Constant.DATA_SOURCE_LOCATION);
        //DefaultResourceLoader可以解析ClassPath或Url，并加载资源文件，得到Resource实例
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        Resource resource = resourceLoader.getResource(databaseLocation);
        //创建YamlPropertiesFactoryBean实例，解析yml文件
        YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
        yamlPropertiesFactoryBean.setResources(resource);
        //创建PropertySourcesPlaceholderConfigurer实例，将属性设置到PropertySourcesPlaceholderConfigurer实例中
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        configurer.setProperties(yamlPropertiesFactoryBean.getObject());
        return configurer;
    }

    /**
     * 初始化DataSourceFactory
     *
     * 这里加了@ConditionalOnMissingBean是为了扩展
     * 因为Microboot早期写死了使用DruidDataSource作为数据源连接池，如果要改的话，牵扯比较多
     * 而且DataSource有很多实现类，它们创建对象和属性赋值的方式与DruidDataSource也不一样，不太好统一
     * 通过@ConditionalOnMissingBean注解可以提供扩展的可能，只需要实现DataSourceFactory接口，如：MyDataSourceFactory
     * 创建MyDataSourceFactory的bean对象，并指定beanName为"org.microboot.data.factory.DataSourceFactory"
     * 这样就可以覆盖掉Microboot默认的DruidDataSourceFactory了
     *
     * @return
     */
    @Bean(name = "org.microboot.data.factory.DataSourceFactory")
    @ConditionalOnMissingBean(name = "org.microboot.data.factory.DataSourceFactory")
    public DataSourceFactory initDataSourceFactory() {
        return new DruidDataSourceFactory();
    }

    /**
     * 初始化DataSource（主库）
     *
     * 说明：
     * Primary注解避免SpringBoot中默认的DataSource通过自动装配的方式创建
     * microboot的数据源配置文件不是用spring.datasource.xxx的方式设置的
     * 这会导致自动装配创建DataSource时报错（找不到url等...）
     * 因此需要手动创建一个master的DataSource覆盖掉默认的DataSource
     *
     * @param dataSourceFactory
     * @return
     */
    @Primary
    @Bean(name = Constant.MASTER_DATA_SOURCE)
    public DataSource initMasterDataSource(DataSourceFactory dataSourceFactory) {
        Map<String, Object> master = dataSourceFactory.getMaster();
        return dataSourceFactory.createDataSource(master);
    }

    /**
     * 初始化NamedParameterJdbcTemplate（主库）
     *
     * @return
     */
    @Bean(name = Constant.MASTER_JDBC_TEMPLATE)
    public NamedParameterJdbcTemplate initMasterJdbcTemplate() {
        DataSource dataSource = ApplicationContextHolder.getBean(Constant.MASTER_DATA_SOURCE, DataSource.class);
        return new NamedParameterJdbcTemplate(dataSource);
    }

    /**
     * 初始化NamedParameterJdbcTemplate（从库）
     *
     * @return
     */
    @Bean(name = Constant.SLAVES_JDBC_TEMPLATE)
    public NamedParameterJdbcTemplate initSlavesJdbcTemplate(DataSourceFactory dataSourceFactory) {
        DataSource dataSource;
        Map<String, Object> slaves = dataSourceFactory.getSlaves();
        if (MapUtils.isEmpty(slaves)) {
            //如果未定义从库，则主从用同一个数据源
            dataSource = ApplicationContextHolder.getBean(Constant.MASTER_DATA_SOURCE, DataSource.class);
        } else {
            //如果定义了从库，则主从分离，单独为从库创建一个数据源【主数据库用来写，从数据库用来读】
            dataSource = dataSourceFactory.createDataSource(slaves);
        }
        return new NamedParameterJdbcTemplate(dataSource);
    }

    /**
     * 初始化Map<String, NamedParameterJdbcTemplate>（其他库）
     *
     * @return
     */
    @Bean(name = Constant.OTHERS_JDBC_TEMPLATE)
    public Map<String, NamedParameterJdbcTemplate> initOthersJdbcTemplate(DataSourceFactory dataSourceFactory) {
        Map<String, NamedParameterJdbcTemplate> othersJdbcTemplateMap = Maps.newConcurrentMap();

        List<Map<String, Object>> others = dataSourceFactory.getOthers();
        //如果未定义其他库，则返回空的othersJdbcTemplateMap
        if (CollectionUtils.isEmpty(others)) {
            return othersJdbcTemplateMap;
        }

        for (Map<String, Object> other : others) {
            DataSource dataSource = dataSourceFactory.createDataSource(other);
            String dataSourceName = dataSourceFactory.getDataSourceName(dataSource);
            othersJdbcTemplateMap.put(dataSourceName, new NamedParameterJdbcTemplate(dataSource));
        }

        return othersJdbcTemplateMap;
    }

    /**
     * 初始化NonDefaultTransactionManagerPostProcessor
     *
     * 尝试过的方法：
     * 1、@ConditionalOnProperty，获取不到database.yml中的属性值【本意是不想把enableXA属性和database属性拆开到不同的配置文件中】
     * 2、根据enableXA选择创建不同的事务管理器【JtaTransactionManager或DataSourceTransactionManager】，但冗余代码太多
     * 3、尝试实现Condition接口或ConfigurationCondition接口，但如果在这些接口中使用DataSourceFactory，则会导致无法正确加载database.yml中的属性值
     * 4、新版中，enableXA属性已经被移除了
     *
     * 从Spring源码的角度来分析：
     * 1、@ConditionalOnProperty注解是在ConfigurationClassPostProcessor中解析的
     * 2、ConfigurationClassPostProcessor是一个BeanDefinitionRegistryPostProcessor，在refresh() -> invokeBeanFactoryPostProcessors(beanFactory)被创建并执行
     * 3、PropertySourcesPlaceholderConfigurer是一个BeanFactoryPostProcessor，在refresh() -> invokeBeanFactoryPostProcessors(beanFactory)被创建并执行
     * 4、BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口
     * 5、invokeBeanFactoryPostProcessors(beanFactory)方法中，执行顺序是有优先级的，如下：
     * 5-1、List<BeanFactoryPostProcessor> beanFactoryPostProcessors中实现了BeanDefinitionRegistryPostProcessor接口的Bean执行postProcessBeanDefinitionRegistry方法
     * 5-2、BeanFactory注册的BeanDefinition中实现了BeanDefinitionRegistryPostProcessor接口的Bean执行postProcessBeanDefinitionRegistry方法
     * 5-3 所有实现了BeanDefinitionRegistryPostProcessor接口的Bean执行postProcessBeanFactory方法
     * 5-4、List<BeanFactoryPostProcessor> beanFactoryPostProcessors中仅实现了BeanFactoryPostProcessor接口的Bean执行postProcessBeanFactory方法
     * 5-5、BeanFactory注册的BeanDefinition中仅实现了BeanFactoryPostProcessor接口的Bean执行postProcessBeanFactory方法
     *
     * 根据上述分析思考方案：
     * · 自定义PropertySourcesPlaceholderConfigurer，实现BeanDefinitionRegistryPostProcessor接口，并通过排序让其在ConfigurationClassPostProcessor之前执行
     * 实际情况：
     * · 当SpringApplication的run方法执行到prepareEnvironment时，会递归一次回到SpringApplication的run方法，然后执行第一遍全流程
     * · 在第一遍全流程时，BeanFactory中只有几个内置的BeanDefinition（包括ConfigurationClassPostProcessor，但不包括PropertySourcesPlaceholderConfigurer及其子类的Bean）
     * 原因说明：
     *   SpringBoot默认情况下根据不同的场景有三个Context类型可供创建，这些Conext在创建时会在BeanFactory中添加一些内置的BeanDefinition
     *   如：ConfigurationClassPostProcessor，AutowiredAnnotationBeanPostProcessor等
     *   这些类很多是BeanFactoryPostProcessor的子类，因此会在invokeBeanFactoryPostProcessors(beanFactory)方法中被提前创建并执行
     *   虽然，PropertySourcesPlaceholderConfigurer也是BeanFactoryPostProcessor的子类，但是SpringBoot的扫描工作是在ConfigurationClassPostProcessor中执行的
     *   因此，PropertySourcesPlaceholderConfigurer的BeanDefinition都还没被创建出来
     *   导致，PropertySourcesPlaceholderConfigurer不会在ConfigurationClassPostProcessor之前被执行
     *   所以，@ConditionalOnProperty注解被解析时，database.yml文件中的配置信息还没有被读取到Environment中来
     *   但是，SpringApplication提供了setApplicationContextClass(...)方法，可以设置自定义的Context
     * 新的思路：
     * · 方案一：自定义ApplicationContextClass，将自定义的PropertySourcesPlaceholderConfigurer添加到List<BeanFactoryPostProcessor> beanFactoryPostProcessors集合中
     * · 方案二：自定义Listener，通过Event指定自定义Listener在refresh()之前就被调用
     * 这两种方案的目的都是为了在@ConditionalOnProperty注解被解析前将database.yml文件中的配置信息读取出来，并设置到环境变量中
     *
     * 下面时方案二的实现：
     * public class MyListener implements GenericApplicationListener {
     *
     *     @Override
     *     public boolean supportsEventType(ResolvableType type) {
     *         Class<?>[] EVENT_TYPES = {
     *                 ApplicationPreparedEvent.class
     *         };
     *         if (type != null) {
     *             for (Class<?> supportedType : EVENT_TYPES) {
     *                 if (supportedType.isAssignableFrom(type.getRawClass())) {
     *                     return true;
     *                 }
     *             }
     *         }
     *         return false;
     *     }
     *
     *     @Override
     *     public void onApplicationEvent(ApplicationEvent event) {
     *         try {
     *             ConfigurableApplicationContext applicationContext = ((ApplicationPreparedEvent) event).getApplicationContext();
     *             ConfigurableEnvironment environment = applicationContext.getEnvironment();
     *
     *             //String databaseLocation = environment.getProperty("microboot.database.location");
     *             String databaseLocation = "file:/{your-path}/database.yml";
     *             //DefaultResourceLoader可以解析ClassPath或Url
     *             ResourceLoader resourceLoader = new DefaultResourceLoader();
     *             Resource resource = resourceLoader.getResource(databaseLocation);
     *             //创建YamlPropertiesFactoryBean实例
     *             YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
     *             yamlPropertiesFactoryBean.setResources(resource);
     *
     *             Properties properties = yamlPropertiesFactoryBean.getObject();
     *             Map<String, Object> map = Maps.newHashMap();
     *             MapPropertySource datasourceMapPropertySource = new MapPropertySource("datasourceMapPropertySource", map);
     *             for (Object oKey : properties.keySet()) {
     *                 map.put((String) oKey, properties.get(oKey));
     *             }
     *             environment.getPropertySources().addLast(datasourceMapPropertySource);
     *         } catch (Exception e) {
     *             e.printStackTrace();
     *         }
     *     }
     * }
     * 解释：
     *  自定义的MyListener通过ApplicationPreparedEvent被指定在refresh()之前执行
     *  即：在@ConditionalOnProperty注解被解析前就将database.yml文件配置信息添加到Environment中
     * 测试：
     *  上述方案使用@ConditionalOnProperty可以读取到database.yml中的datasource.enableXA这个配置了
     * 问题：
     *  上面方案中的database.yml的路径是写死的，因为此时Environment还没有解析application.yml文件，自然也就拿不到application.yml中指定的database.yml的地址了
     * 解决：
     *  由于Environment在初始化时会加载系统环境变量，所以可以通过 System.setProperty()的方式将database.yml的文件地址设置进去
     *  而database.yml的文件地址则可以通过main函数的参数传进来，这是一种思路
     * 补充：
     *  新版中，enableXA属性已经被移除了
     *
     *
     * @param dataSourceFactory
     * @return
     */
    @Bean
    public NonDefaultTransactionManagerPostProcessor initNonDefaultTransactionManagerPostProcessor(DataSourceFactory dataSourceFactory) {
        if (dataSourceFactory.getEnableXA()) return null;
        return new NonDefaultTransactionManagerPostProcessor();
    }

    /**
     * 初始化DefaultTransactionManagerPostProcessor
     *
     * @return
     */
    @Bean
    public DefaultTransactionManagerPostProcessor initDefaultTransactionManagerPostProcessor() {
        return new DefaultTransactionManagerPostProcessor();
    }

    /**
     * 初始化freemarker.template.Configuration
     *
     * @param dataSourceFactory
     * @return
     * @throws Exception
     */
    @Bean(name = Constant.FREEMARKER_CONFIGURATION)
    public freemarker.template.Configuration initConfiguration(DataSourceFactory dataSourceFactory, FreeMarkerProperties properties) throws Exception {
        freemarker.template.Configuration configuration = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_24);

        //本来想完全交给SpringBoot去管理，直接在配置文件中写配置信息即可
        //但是测试发现beansWrapperFn会报错，可能是因为不是在构建Bean的时候设置的
        //所以参考SpringBoot的构建方式，手动创建，现在settings属性还是在配置文件中设置
        configuration.setDefaultEncoding(properties.getCharsetName());
        Properties settings = new Properties();
        settings.putAll(properties.getSettings());
        configuration.setSettings(settings);
        //用于整合Java工具类
        Map<String, Object> variables = Maps.newHashMap();
        variables.put("beansWrapperFn", new BeansWrapperBuilder(freemarker.template.Configuration.VERSION_2_3_24).build());
        configuration.setSharedVariables(variables);
		/*
             1、autoImport是用key-value的方式匹配需要自动加载的模板，即：使用一个变量指向一个模板
             2、传统方式：
                    freemarker加载模板位置是在classpath:/templates/，查看FreeMarkerProperties这个类
                    autoImport的value是freemarker模板文件的相对路径，比如：
                        classpath:/templates/sql/macro.sql -> autoImport.put("macro", "sql/macro.sql");
             3、microboot框架改变了freemarker模板的加载位置（兼容打jar包后读取模板），查看TemplateResolver这个类
                所以此时autoImport的value是freemarker模板文件加载到内存后的key值，如下：
                autoImport.put("macro", "macro"); value值的macro是模板加载时的templateKey
             4、autoImport.put("key", "value")
                    key：表示指向value这个模板
                    value：表示模板名【默认是用模板的相对路径做templateKey】，TemplateResolver将模板名本身作为templateKey
                如果加载的macro.sql改名叫XXX.sql，那么basedao-*.yml文件中的datasource.macro就应该配置为XXX
                那么这里的autoImport的key还是macro，value就是XXX
		 */
        String macro = dataSourceFactory.getMacro();
        if (StringUtils.isNotBlank(macro)) {
            Map<String, Object> autoImport = Maps.newHashMap();
            autoImport.put("macro", macro);
            configuration.setAutoImports(autoImport);
        }
        return configuration;
    }

    /**
     * 初始化TemplateResolver
     *
     * @return
     */
    @Bean(name = "org.microboot.data.resolver.TemplateResolver")
    public TemplateResolver initTemplateResolver() {
        return new TemplateResolver();
    }

    /**
     * 初始化BaseDao
     *
     * @return
     */
    @Bean(name = "org.microboot.data.basedao.BaseDao")
    public BaseDao initBaseDao() {
        return new BaseDao();
    }
}